import { IHeapSnapshot } from "@memlab/core";
import { ArkAnalyzer } from "./ArkAnalyzer";

/**
 * Ark Heapsnapshot 内存泄漏分析器
 */
export class ArkLeakAnalyzer extends ArkAnalyzer {
    public readonly DOMAIN = 'meminsight';
    public readonly TAG = ArkLeakAnalyzer.name;
    public static readonly NAME = 'ark-leak-analyzer';
    public static readonly DESCRIPTION = "Analyze heapsnapshot files and find leaks.";

    protected snapshotList: IHeapSnapshot[];

    constructor() {
        super();
        this.type = 'ark-leak-analyzer';
    }

    // public override async analyze(args: any[]) : Promise<any> {
    //     if (args.length < 3) {
    //         console.log(`Argument count ${args.length} is not enough.`);
    //         return undefined;
    //     }
    //     let filepath = args[0] as string;
    //     if (!fs.existsSync(filepath)) {
    //         console.log(`${filepath} not found.`);
    //         return undefined;
    //     }
    //     this.snapshot = await Loader.loadFromFileOption(filepath, { buildNodeIdIndex: true, });
    //     return this.snapshot;
    // }

    // private async analyzeByFilter(snapshot: string, output: string, type: string, 
    //     filter?: (cfg: any) => boolean): Promise<void> {
    //     const result: ILeakResult[] = [];
    //     const leakUnits: ILeakUnit[] = [];
    //     const filterNodeIds: number[] = [];
        
    //     const heapSnapshot = await Loader.loadFromFile(snapshot, true);
    //     const leakedNodesIds = ArkCompLeaksProc.getLeakedNodeIds(heapSnapshot, true);
    //     const leakedTracePathes : Map<number, LeakTracePathItem> = 
    //         ArkCompLeaksProc.getLeakedTraces(heapSnapshot, leakedNodesIds);

    //     for (const [leakedNodeId, leakItemPath] of leakedTracePathes) {
    //         const leakedNode = heapSnapshot.getNodeById(leakedNodeId);
    //         if (filter && !filter(leakedNodeId, leakedNode?.type || '')) {
    //             filterNodeIds.push(leakedNodeId);
    //             continue;
    //         }
    //         const leakUnit: ILeakUnit = {
    //             nodeId: leakedNodeId,
    //             startTime: -1,
    //             endTime: -1,
    //             duration: -1,
    //             trace: ArkCompLeaksProc.leakTracePathToStrings(heapSnapshot, leakItemPath)
    //         };
    //         leakUnits.push(leakUnit);
    //     }

    //     if (filterNodeIds.length > 0) {
    //         console.log(`[Ignored Leaks: ${filterNodeIds.length}]`);
    //         filterNodeIds.forEach((nodeId) => {
    //             const node = heapSnapshot.getNodeById(nodeId);
    //             console.log(`[${node.name} @${nodeId}]`);
    //         });
    //     }

    //     const related = new Map<any, any>();
    //     related.set("snapshot", snapshot);

    //     result.push(
    //         { 
    //             leaks: leakUnits,
    //         }
    //     );
    //     console.log(`[Leaks Found: ${leakUnits.length}]`);
    // }

    // /**
    //  * 获取组件泄露的节点
    //  * @param snapshot heapsnapshot graph
    //  * @param isPrint 是否打印泄露的组件对象
    //  * 
    //  * @returns 组件泄露的节点集
    //  */
    // private static getLeakedNodeIds(snapshot: IHeapSnapshot, isPrint: boolean = false) : Set<number> {
    //     let result: Set<number> = new Set<number>();
    //     let arkCompMap: Map<string, Set<number>> = new Map<string, Set<number>>();
    //     snapshot.nodes.forEach((node: IHeapNode) => {
    //         let edges = node.references.filter((edge, ) => edge.name_or_index == STRINGS.IS_DELETING);
    //         if (edges.length > 0 && edges[0].toNode.name === STRINGS.BOOLEAN_TRUE) {
    //             result.add(node.id);
    //             ArkCompLeaksProc.setLeaks(arkCompMap, node);
    //         }
    //     });
    //     if (isPrint) {
    //         ArkTracer.outputTracePath(path, (path) => {
    //             this.printTracePath(path);
    //         });
    //         Log.infoX(this, 'The shortest path to GC roots:');
    //         let curPath = path;
    //         let curNode = path.node;
    //         while (curPath && curNode) { 
    //             console.log(`(@${curNode.id})${curNode.name})`);
    //             curPath = curPath.next;
    //             curNode = curPath.next?.node;
    //             if (curNode) {
    //                 console.log(`\t===>`); 
    //             }        
    //         }
    //     }
    //     return result;
    // } 
 
    // private static setLeaks(compMap: Map<string, Set<number>>, node: IHeapNode) {
    //     let nodeIds: Set<number> | undefined = compMap.get(node.name);
    //     if (nodeIds === undefined) {
    //         nodeIds = new Set<number>();
    //     }
    //     nodeIds.add(node.id);
    //     compMap.set(node.name, nodeIds);
    // }

    // private static printLeaks(nodeIds: Set<number>, 
    //     compMap: Map<string, Set<number>>, 
    //     scene?: string, 
    //     region?: string) {
    //     let sceneStr = scene || `ArkComponent-Leak`;
    //     let regionStr = region || `UI`;
    //     console.log(`[${regionStr}-${sceneStr}]: ${nodeIds.size}`);
    //     if (compMap.size > 0) {
    //         const MAX_LEN = 10;
    //         for (const [name, ids] of compMap) {
    //             const idArray = Array.from(ids);
    //             const truncatedIds = idArray.length > MAX_LEN 
    //                 ? idArray.slice(0, MAX_LEN).concat(["..."]) 
    //                 : idArray;
    //             console.log(`${name}-${ids.size}: @${JSON.stringify(truncatedIds)}`);
    //         }
    //     }
    // }

    // private static getLeakedTraces(snapshot: IHeapSnapshot, nodeIds: Set<number>) : Map<number, LeakTracePathItem> {
    //     const leakedTracePathes = new Map<number, LeakTracePathItem>
    //     let finder = new TraceFinder();
    //     finder.annotateShortestPaths(snapshot); // shortest path for all nodes
    //     utils.markAllDetachedFiberNode(snapshot);
    //     utils.markAlternateFiberNode(snapshot);
    //     utils.applyToNodes(nodeIds, snapshot, node => {
    //         let tracePath = finder.getPathToGCRoots(snapshot, node);
    //         if (tracePath) leakedTracePathes.set(node?.id, tracePath);
    //     });
    //     return leakedTracePathes;
    // }

    // public static leakTracePathToStrings(snapshot: IHeapSnapshot, trace: LeakTracePathItem) : string[] {
    //     return serializer.summarizePath(trace, new Set<number>(), snapshot).split('\n').map(str => {
    //         const nodeIdStr: string = str.substring(str.indexOf('@') + 1).split(' ')[0];
    //         if (Number.isNaN(nodeIdStr)) {
    //             return str;
    //         }
    //         const nodeId: number = Number.parseInt(nodeIdStr);
    //         const node: Optional<IHeapNode> = snapshot.getNodeById(nodeId);
    //         if (!node) {
    //             return str;
    //         }
    //         if (str.indexOf(STRINGS.OBJECT) >= 0) {
    //             return `${str} ${ArkCompLeaksProc.getDetails(node)}`
    //         }
    //         if (str.indexOf(STRINGS.CLOSURE) >= 0) {
    //             return `${str} <${node.name}>`
    //         }
    //         return str;
    //     });
    // }

    // public static getDetails(node: IHeapNode): string {
    //     if (node.type !== 'object' || node?.references?.length === undefined) {
    //         return '';
    //     }
    //     while (node.references.length > 0) {
    //         const ctor_edge = node.references.find((edge) => edge.name_or_index === STRINGS.CONSTRUCTOR);
    //         if (ctor_edge) {
    //             return `<${ctor_edge.toNode.name}>`;
    //         }
    //         const proto_edge = node.references.find((edge) => edge.name_or_index === STRINGS.PROTO);
    //         if (proto_edge) {
    //             node = proto_edge.toNode;
    //         } else {
    //             return '';
    //         }
    //     }
    //     return '';
    // }
}